package msu.ml.data;

import msu.ml.core.*;
import weka.core.*;
import java.io.*;
import java.util.*;

/**
 * The manual validation data provider allows a developer/user to manually
 * specify the number of training/target sets and which files to use in each
 * of those sets.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class DbManualValidationDataProvider implements IDbDataProvider 
{

    private String directoryPath;
    private DatabaseCache cache;
    private ArrayList<IDataPreProcessor> filters;
    private ArrayList<ValidationSet> validationSets;
    private int currentIndex;

    public DbManualValidationDataProvider()
    {
        this(false);
    }

    public DbManualValidationDataProvider(boolean clearCache)
    {
        this.cache = new DatabaseCache("jdbc:mysql://localhost/nexrad",
         "rmead", "");
        if(clearCache)
            this.cache.clear();
        this.validationSets = new ArrayList<ValidationSet>();
        this.filters = new ArrayList<IDataPreProcessor>();
        this.currentIndex = 0;
    }

    public void addFilter(IDataPreProcessor filter)
    {
        this.filters.add(filter);
    }

    public void clearFilters()
    {
        this.filters.clear();
    }

    public void reset()
    {
        this.currentIndex = 0;
    }

    public int count()
    {
        int count = 0;
        for(int i = 0; i < validationSets.size(); i++)
        {
            ValidationSet current = this.validationSets.get(i);
            count += current.targetFiles.length;
        }

        return count;
    }

    /**
     * returns the directory path to the files.
     * @return directory path
     */
    public String getDirectoryPath()
    {
        return directoryPath;
    }

    /**
     * add a new validation set to the list of sets that will be returned
     * when making calls to getNextTrainingData and getNextTargetData.
     *
     * @param trainingFiles array of training files
     * @param targetFiles array of target files
     */
    public void addValidationSet(String [] trainingFiles, String [] targetFiles)
    {
        this.validationSets.add(new ValidationSet(trainingFiles, targetFiles));
    }


    /**
     * Provides the next group of target data instances.
     * Unlike the training data which can be aggregated into
     * one monolythic instances object, the target data
     * is provides as an array of instances, each representing
     * a single sweep. In this way the data can be classified
     * one sweep at a time.
     *
     * @return next group of target data instances
     */
    public NxInstances [] getNextTargetData()
    {
        ValidationSet current = this.validationSets.get(currentIndex);

        NxInstances [] result = new NxInstances[current.targetFiles.length];
        for(int i = 0; i < result.length; i++)
            result[i] = this.cache.retrieve(current.targetFiles[i], filters);

        currentIndex++;
        return result;
    }

    /**
     * Get the names of the data sources used in the
     * next set of training data.
     *
     * @return the labels of the next training data
     */
    public String [] getNextTrainingLabels()
    {
        ValidationSet current = this.validationSets.get(currentIndex);

        String [] result = new String[current.trainingFiles.length];
        for(int i = 0; i < current.trainingFiles.length; i++)
            result[i] = current.trainingFiles[i];

        return result;
    }

    /**
     * Provides the next set of training instances.
     *
     * @return next training data
     */
    public NxInstances [] getNextIndividualTrainingData()
    {
        ValidationSet current = this.validationSets.get(currentIndex);

        NxInstances [] result = new NxInstances[current.trainingFiles.length];
        for(int i = 0; i < result.length; i++)
            result[i] = this.cache.retrieve(current.trainingFiles[i], filters);

        return result;
    }

    /**
     * Provides the next set of training instances in one
     * large Instances object.
     *
     * @return next training data
     */
    public NxInstances getNextTrainingData()
    {
        ValidationSet current = this.validationSets.get(currentIndex);

        NxInstances result = null;
        for(int i = 0; i < current.trainingFiles.length; i++)
        {
            if(result == null)
                result = this.cache.retrieve(current.trainingFiles[i], filters);
            else
                result.concatenate(this.cache.retrieve(current.trainingFiles[i], filters));
        }

        return result;
    }

    /**
     * Get the number of validation sets
     *
     * @return the number of validation sets
     */
    public int getNumValidationSets()
    {
        return this.validationSets.size();
    }

    /**
     * Return whether there is more data 
     *
     * @return true if more data exists
     */
    public boolean hasMoreData()
    {
        return currentIndex < validationSets.size();
    }

    public DatabaseCache getCache()
    {
        return this.cache;
    }

    public String [] getFileNames()
    {

        if(this.validationSets.size() > 0)
            return this.validationSets.get(0).targetFiles;

        return null;
    }

    /**
     * set the directory path to the files
     *
     * @param value directory path
     */
    public void setDirectoryPath(String value)
    {
        directoryPath = value;
        this.cache.setRawDataDirectory(value);
    }

    private class ValidationSet
    {
        public String [] trainingFiles;
        public String [] targetFiles;

        public ValidationSet(String [] trainingFiles, String [] targetFiles)
        {
            this.trainingFiles = new String[trainingFiles.length];
            this.targetFiles = new String[targetFiles.length];
            System.arraycopy(trainingFiles, 0, this.trainingFiles, 0, trainingFiles.length);
            System.arraycopy(targetFiles, 0, this.targetFiles, 0, targetFiles.length);
        }

    }

    public void initialize()
    {

    }
}
